using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics;
using IfacesEnumsStructsClasses;

/*

 
AppControl demo application -
A simple application to demonstrate the usage of the new features.
It includes a very simple Synch and threaded downloaders to download resources and
upon completion notifies the csEXWB client.
It also contains a simple example of custom protocol usage and handling.


IMPORTANT:
==========
Custom protocol handler needs access to registry.
StartCustomProtocol - Activates a custom protocol by registering CLSID of the CCustManageApp under "HKEY_CLASSES_ROOT\\PROTOCOLS\\Handler\\" registry key for the target protocol (mydata)
StopCustomProtocol - Deactivates a previously activated custom protocol by deleting the registry key added to "HKEY_CLASSES_ROOT\\PROTOCOLS\\Handler\\"

NOTE:
AppControl uses a temp folder (System default temporary folder) to act as a temp cache.
Please make sure before running the AppControl to adjust the name of the folder.
The temp folder is created and deleted in load and unload events of the Form1.

 
By default, Protocol Handlers are activated internaly when the Internet_Explorer Server handle is available.
Flow of execution:

    Download Request Handled by Client:
        Client subscribes to (activates) DownloadRequestAsynch and/or DownloadRequestSynch events
            Asynch -Control Fires-> DownloadRequestAsynch
                NeedFileName
                    -Client Calls->
                        AsynchDownloadRequestEventArgs.DownloadCompletedNeedFileName
                NeedData
                    -Client Calls->
                        AsynchDownloadRequestEventArgs.DownloadCompleted
            Synch -Control Fires-> DownloadRequestSynch
                NeedFilename
                    -Client Sets->
                        SynchDownloadRequestEventArgs.CacheFileName
                        SynchDownloadRequestEventArgs.Url
                        SynchDownloadRequestEventArgs.DownloadDataMimeType
                NeedData
                    -Client Sets->
                        SynchDownloadRequestEventArgs.DownloadData
                        SynchDownloadRequestEventArgs.Url
                        SynchDownloadRequestEventArgs.DownloadDataMimeType

    Download Request Handled by Default Implementation:
        Client subscribes to (activates) ProtocolHandlerBeginTransaction and/or ProtocolHandlerOnResponse events
            -Control Fires-> ProtocolHandlerBeginTransaction (Client has achance to add additional request headers)
            -Control Fires-> ProtocolHandlerOnResponse

To cancel a request:
	Asynch (in DownloadRequestAsynch event handler): 
		-Client Calls-> AsynchDownloadRequestEventArgs.CancelDownloadRequest
	Synch (in DownloadRequestSynch event handler):
		-Client Sets to true-> SynchDownloadRequestEventArgs.CancelDownload

To have the default iplementation download a resource:
	-In DownloadRequestAsynch and DownloadRequestSynch event handlers
	-Client Sets-> xxxxDownloadRequestEventArgs.ClientHandled to false
 
 */

namespace AppControl
{
    public partial class Form1 : Form
    {
        //Replace with your local cache directory
        //An attempt is made to create temp folder in Form1_Load and delete it in Form1_FormClosing
        //Before each navigate, all files in the directory are deleted
        private string m_CachePath = string.Empty; //Using system default temporary folder

        //Sample data to load as an example for custom protocols
        private const string m_testdata = "<HTML><HEAD></HEAD><BODY><P>This is test data for a custom protocol handler</P></BODY></HTML>";

        public Form1()
        {
            InitializeComponent();

            //Asynch notification
            this.cEXWB1.DownloadRequestAsynch += new csExWB.AsynchDownloadRequestEventHandler(cEXWB1_DownloadRequestAsynch);
            //Synch notification
            this.cEXWB1.DownloadRequestSynch += new csExWB.SynchDownloadRequestEventHandler(cEXWB1_DownloadRequestSynch);

            //Custom protocol notification
            this.cEXWB1.CustomDownloadRequest += new csExWB.CustomDownloadRequestEventHandler(cEXWB1_CustomDownloadRequest);

            this.cEXWB1.BeforeNavigate2 += new csExWB.BeforeNavigate2EventHandler(cEXWB1_BeforeNavigate2);
            this.cEXWB1.DocumentComplete += new csExWB.DocumentCompleteEventHandler(cEXWB1_DocumentComplete);
            this.cEXWB1.TitleChange += new csExWB.TitleChangeEventHandler(cEXWB1_TitleChange);
            this.cEXWB1.NavigateError += new csExWB.NavigateErrorEventHandler(cEXWB1_NavigateError);
            this.cEXWB1.ScriptError += new csExWB.ScriptErrorEventHandler(cEXWB1_ScriptError);
            this.cEXWB1.WBSecurityProblem += new csExWB.SecurityProblemEventHandler(cEXWB1_WBSecurityProblem);
            this.cEXWB1.WBDocHostShowUIShowMessage += new csExWB.DocHostShowUIShowMessageEventHandler(cEXWB1_WBDocHostShowUIShowMessage);

            this.button1.Click += new EventHandler(button1_Click); //Navigate
            this.button2.Click += new EventHandler(button2_Click); //Activate custom APP
            this.button3.Click += new EventHandler(button3_Click); //Deactivate custom APP
            
            this.Load += new EventHandler(Form1_Load);
        }

        #region Webbrowser Events

        void cEXWB1_DocumentComplete(object sender, csExWB.DocumentCompleteEventArgs e)
        {
            this.txtLog.AppendText("\r\n======>DocumentComplete URL >> " + e.url);
        }

        void cEXWB1_BeforeNavigate2(object sender, csExWB.BeforeNavigate2EventArgs e)
        {
            this.txtLog.AppendText("\r\n======>BeforeNavigate2 URL >> " + e.url);
        }

        void cEXWB1_CustomDownloadRequest(object sender, csExWB.CustomDownloadRequestEventArgs e)
        {
            //Can process different custom protocols at the same time
            //if (e.Protocol == "mydata")
            //{
            byte[] m_mydata = UTF8Encoding.UTF8.GetBytes(m_testdata);
            e.DownloadCompleted(m_mydata, e.Url, "text/html");
            //e.CancelDownloadRequest((int)WinInetErrors.INET_E_DATA_NOT_AVAILABLE);
            //}
        }

        void cEXWB1_DownloadRequestAsynch(object sender, csExWB.AsynchDownloadRequestEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(StartDownload, e);
            //if (e.Url.EndsWith(".dll"))
            //{
            //    ThreadPool.QueueUserWorkItem(StartDownload, e);
            //}
            //else //Let the default implementation download the rest
            //    e.ClientHandled = false;

        }

        void cEXWB1_DownloadRequestSynch(object sender, csExWB.SynchDownloadRequestEventArgs e)
        {
            HttpWebRequest request = null;
            try
            {
                //i.e. Getting m_Url as "http://:/" when doing a search in yahoo
                request = (HttpWebRequest)HttpWebRequest.Create(e.Url);
            }
            catch (Exception)
            {
                e.CancelDownload = true;
                return;
            }

            request.Referer = e.Referer;

            if (e.Post == null)
            {
                request.Method = "GET";
            }
            else
            {
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";

                //To get rid of trailing null, i guess?
                int length = e.Post.Length;
                if (e.Post[length - 1] == 0) length--;

                request.ContentLength = length;

                Stream s = request.GetRequestStream();
                s.Write(e.Post, 0, length);

                s.Close();
            }

            try
            {
                HttpWebResponse response = null;
                //We get http errors here so we can abort if needed
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                }
                catch (System.Net.ProtocolViolationException)
                {
                    //Send an abort
                    e.CancelDownload = true;
                    return;
                }
                catch (System.Net.WebException) //We are getting some exceptions here
                {
                    //Normally 404, not found
                    //Debug.Print("=============EX=>"+ aa.Message);
                    //Send an abort
                    e.CancelDownload = true;
                    return;
                }

                Stream s = response.GetResponseStream();

                byte[] temp = ReadFully(s, 0);
                s.Close();

                if ((temp == null) || (temp.Length == 0))
                    e.CancelDownload = true;
                else
                {
                    //Report the full path to local file and save all downloaded data
                    //This is very important
                    if (e.NeedFileName)
                    {
                        string cachename = e.Url.Substring(e.Url.LastIndexOf('/') + 1);

                        //Watch out for invalid path and filename characters
                        try
                        {
                            if (!string.IsNullOrEmpty(Path.GetFileName(cachename)))
                                File.WriteAllBytes(m_CachePath + cachename, temp);
                        }
                        catch (ArgumentException)
                        {
                            Random rd = new Random();
                            //Use a temp file name
                            cachename = "Tempfile" + rd.Next();
                            File.WriteAllBytes(m_CachePath + cachename, temp);
                        }
                        e.CacheFileName = m_CachePath + cachename;
                    }
                    else
                        //Do not need this - seems to load it from local file
                        //need more testing
                        e.DownloadData = temp;

                    e.Url = response.ResponseUri.AbsoluteUri;
                    e.DownloadDataMimeType = response.ContentType.Split(';')[0];
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("cEXWB1_DownloadRequestSynch===>\r\n" + ex.ToString());
            }
        }

        void cEXWB1_TitleChange(object sender, csExWB.TitleChangeEventArgs e)
        {
            this.Text = e.title;
        }

        void cEXWB1_NavigateError(object sender, csExWB.NavigateErrorEventArgs e)
        {
            this.txtLog.AppendText("\r\ncEXWB1_NavigateError=====" + e.statuscode.ToString());
        }

        void cEXWB1_ScriptError(object sender, csExWB.ScriptErrorEventArgs e)
        {
            this.txtLog.AppendText("\r\ncEXWB1_ScriptError=====" + e.errorMessage + "=" + e.lineNumber.ToString());
        }

        void cEXWB1_WBSecurityProblem(object sender, csExWB.SecurityProblemEventArgs e)
        {
            this.txtLog.AppendText("\r\ncEXWB1_WBSecurityProblem=====" + e.problem.ToString());
        }

        void cEXWB1_WBDocHostShowUIShowMessage(object sender, csExWB.DocHostShowUIShowMessageEventArgs e)
        {
            this.txtLog.AppendText("\r\ncEXWB1_WBDocHostShowUIShowMessage=====" + e.text);
        }
        
        #endregion

        #region Form Events

        void Form1_Load(object sender, EventArgs e)
        {
            //Set up temp folder
            m_CachePath = Path.GetTempPath() + "TempCache\\";
            //Create if does not exists
            if (!Directory.Exists(m_CachePath))
                Directory.CreateDirectory(m_CachePath);
            else //else clean it
                ClearCacheFolder(false);
            this.comboBox1.SelectedIndex = 0;
            this.cEXWB1.NavToBlank();
        }

        void ClearCacheFolder(bool deletecache)
        {
            //Clear cache folder?
            if (Directory.Exists(m_CachePath))
            {
                string[] files = Directory.GetFiles(m_CachePath);
                if ((files != null) && (files.Length > 0))
                {
                    foreach (string item in files)
                    {
                        try
                        {
                            File.Delete(item);
                        }
                        catch (Exception ee)
                        {
                            Debug.Print("ClearCacheFolder-Exception===> " + ee.ToString());
                        }
                    }
                }
                if (deletecache)
                    Directory.Delete(m_CachePath);
            }
        }

        void button1_Click(object sender, EventArgs e)
        {
            ClearCacheFolder(false);
            this.cEXWB1.Navigate(this.comboBox1.Text);
        }

        void button2_Click(object sender, EventArgs e)
        {
            //Activate as many, well within reason, custom protocols as you need
            //They will all raise cEXWB1_CustomDownloadRequest event
            this.cEXWB1.StartCustomProtocol(textBox1.Text);
            comboBox1.Text = textBox1.Text + "://someurl";
        }

        void button3_Click(object sender, EventArgs e)
        {
            this.cEXWB1.StopCustomProtocol(textBox1.Text);
        }
 
        //Clear log
        private void button4_Click(object sender, EventArgs e)
        {
            txtLog.Clear();
        }

        //Save log
        private void button5_Click(object sender, EventArgs e)
        {
            SaveFileDialog m_dlgSave = new SaveFileDialog();
            m_dlgSave.DefaultExt = "txt";
            m_dlgSave.FileName = "FileName";
            m_dlgSave.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            m_dlgSave.Title = "Save File";
            m_dlgSave.InitialDirectory = Environment.CurrentDirectory;
            if (m_dlgSave.ShowDialog(this) == DialogResult.OK)
            {
                using (StreamWriter sw = new StreamWriter(m_dlgSave.FileName))
                {
                    sw.Write(txtLog.Text);
                }
            }
        }

        #endregion

        #region Helper Methods
        /// <summary>
        /// Method that performs the download on a background thread
        /// and sends notification to the COM lib protocol handler
        /// </summary>
        /// <param name="state"></param>
        void StartDownload(object state)
        {
            int err = unchecked((int)0x800C0002); //INET_E_INVALID_URL
            csExWB.AsynchDownloadRequestEventArgs e = (csExWB.AsynchDownloadRequestEventArgs)state;

            HttpWebRequest request = null;
            try
            {
                //i.e. Getting m_Url as "http://:/" when doing a search in yahoo
                request = (HttpWebRequest)HttpWebRequest.Create(e.Url);
            }
            catch (Exception)
            {
                e.CancelDownloadRequest(err);
                return;
            }

            request.Referer = e.Referer;

            if (e.Post == null)
            {
                request.Method = "GET";
            }
            else
            {
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";

                //To get rid of trailing null, i guess?
                int length = e.Post.Length;
                if (e.Post[length - 1] == 0) length--;

                request.ContentLength = length;

                Stream s = request.GetRequestStream();
                s.Write(e.Post, 0, length);

                s.Close();
            }


            try
            {
                HttpWebResponse response = null;
                //We get http errors here so we can abort if needed
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                }
                catch (System.Net.ProtocolViolationException)
                {
                    //Send an abort
                    e.CancelDownloadRequest(err);
                    return;
                }
                catch (System.Net.WebException) //We are getting some exceptions here
                {
                    //Send an abort
                    e.CancelDownloadRequest(err);
                    return;
                }

                Stream s = response.GetResponseStream();

                byte[] temp = ReadFully(s, 0);
                s.Close();

                //Modified to account for temp having some garbage data but the Length is being reported as 0???
                if ((temp == null) || (temp.Length == 0))
                    e.CancelDownloadRequest(err);
                else
                {
                    //Need a local file, save it here and pass the filename
                    if (e.NeedFileName)
                    {
                        string cachename = e.Url.Substring(e.Url.LastIndexOf('/') + 1);
                        //Watch out for invalid path and filename characters
                        try
                        {
                            if (!string.IsNullOrEmpty(Path.GetFileName(cachename)))
                            {
                                cachename = m_CachePath + cachename;
                                File.WriteAllBytes(cachename, temp);
                            }
                        }
                        catch (ArgumentException)
                        {
                            Random rd = new Random();
                            //Use a temp file name
                            cachename = m_CachePath + "Tempfile" + rd.Next();
                            File.WriteAllBytes(cachename, temp);
                        }
                        e.DownloadCompletedNeedFileName(response.ResponseUri.AbsoluteUri, response.ContentType.Split(';')[0], cachename);
                    }
                    else
                        e.DownloadCompleted(temp, response.ResponseUri.AbsoluteUri, response.ContentType.Split(';')[0]);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("StartDownload===>\r\n" + ex.ToString());
            }
        }

        /// <summary>
        /// Reads data from a stream until the end is reached. The
        /// data is returned as a byte array. An IOException is
        /// thrown if any of the underlying IO calls fail.
        /// </summary>
        /// <param name="stream">The stream to read data from</param>
        /// <param name="initialLength">The initial buffer length</param>
        public byte[] ReadFully(Stream stream, int initialLength)
        {
            // If we've been passed an unhelpful initial length, just
            // use 32K.
            if (initialLength < 1)
            {
                initialLength = 32768;
            }

            byte[] buffer = new byte[initialLength];
            int read = 0;

            int chunk;
            while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
            {
                read += chunk;

                // If we've reached the end of our buffer, check to see if there's
                // any more information
                if (read == buffer.Length)
                {
                    int nextByte = stream.ReadByte();

                    // End of stream? If so, we're done
                    if (nextByte == -1)
                    {
                        return buffer;
                    }

                    // Nope. Resize the buffer, put in the byte we've just
                    // read, and continue
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);
                    newBuffer[read] = (byte)nextByte;
                    buffer = newBuffer;
                    read++;
                }
            }
            // Buffer is now too big. Shrink it.
            byte[] ret = new byte[read];
            Array.Copy(buffer, ret, read);
            return ret;
        }

        /// <summary>
        /// Helper method.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        byte[] ReadStreamToEnd(Stream stream)
        {
            byte[] buffer = new byte[32768]; //32K chunks
            using (MemoryStream ms = new MemoryStream())
            {
                while (true)
                {
                    int read = stream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                        return ms.ToArray();
                    ms.Write(buffer, 0, read);
                }
            }

        } 
        #endregion



    }
}